/**
 * Copyright (C) 2018-2021
 * All rights reserved, Designed By www.yixiang.co
 */
package com.zgjkhis.modules.security.rest;

import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wf.captcha.ArithmeticCaptcha;
import com.zgjkhis.annotation.AnonymousAccess;
import com.zgjkhis.exception.ServiceException;
import com.zgjkhis.logging.aop.log.Log;
import com.zgjkhis.modules.security.config.SecurityProperties;
import com.zgjkhis.modules.security.security.LoginUserUtil;
import com.zgjkhis.modules.security.security.TokenUtil;
import com.zgjkhis.modules.security.security.vo.AuthUser;
import com.zgjkhis.modules.security.security.vo.JwtUser;
import com.zgjkhis.modules.security.security.vo.SwitchStoreReq;
import com.zgjkhis.modules.security.service.OnlineUserService;
import com.zgjkhis.modules.system.domain.Store;
import com.zgjkhis.modules.system.domain.User;
import com.zgjkhis.modules.system.service.StoreService;
import com.zgjkhis.modules.system.service.UserService;
import com.zgjkhis.modules.util.CharUtil;
import com.zgjkhis.utils.RedisUtils;
import com.zgjkhis.utils.SecurityUtils;
import com.zgjkhis.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @date 2018-11-23
 * 授权、根据token获取用户详细信息
 */
@Slf4j
@RestController
@RequestMapping("/auth")
@Api(tags = "系统：系统授权接口")
public class AuthController {

    private final SecurityProperties properties;
    private final RedisUtils redisUtils;
    private final UserDetailsService userDetailsService;
    private final OnlineUserService onlineUserService;
    private final TokenUtil tokenUtil;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;
    private final UserService userService;
    private final StoreService storeService;
    @Value("${loginCode.expiration}")
    private Long expiration;
    @Value("${rsa.private_key}")
    private String privateKey;
    @Value("${single.login}")
    private Boolean singleLogin;

    public AuthController(SecurityProperties properties, RedisUtils redisUtils, UserDetailsService userDetailsService, OnlineUserService onlineUserService, TokenUtil tokenUtil, AuthenticationManagerBuilder authenticationManagerBuilder, UserService userService, StoreService storeService) {
        this.properties = properties;
        this.redisUtils = redisUtils;
        this.userDetailsService = userDetailsService;
        this.onlineUserService = onlineUserService;
        this.tokenUtil = tokenUtil;
        this.authenticationManagerBuilder = authenticationManagerBuilder;
        this.userService = userService;
        this.storeService = storeService;
    }

    @Log("用户登录")
    @ApiOperation("登录授权")
    @AnonymousAccess
    @PostMapping(value = "/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUser authUser, HttpServletRequest request) {
        String password = authUser.getPassword();
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new ServiceException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new ServiceException("验证码错误");
        }
        Map<String, Object> authInfo = startLogin(authUser.getUsername(), password, authUser.getPharmacyId(), request);
        return ResponseEntity.ok(authInfo);
    }

    private Map<String, Object> startLogin(String userName, String password, String pharmacyId, HttpServletRequest request) {
        // 密码解密
        RSA rsa = new RSA(privateKey, null);
        password = new String(rsa.decrypt(password, KeyType.PrivateKey));
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(userName, password);

        redisUtils.set(userName + "_login", pharmacyId, 24, TimeUnit.HOURS);

        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        final UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        String token = tokenUtil.generateToken(userDetails);
        final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUser, token, request);
        // 返回 token 与 用户信息
        Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUser);
        }};
        if (singleLogin) {
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(userName, token);
        }

        return authInfo;
    }

    @ApiOperation("获取用户信息")
    @GetMapping(value = "/info")
    public ResponseEntity<Object> getUserInfo() {
        JwtUser jwtUser = (JwtUser) userDetailsService.loadUserByUsername(SecurityUtils.getUsername());
        return ResponseEntity.ok(jwtUser);
    }

    @AnonymousAccess
    @ApiOperation("获取验证码")
    @GetMapping(value = "/code")
    public ResponseEntity<Object> getCode() {
        // 算术类型 https://gitee.com/whvse/EasyCaptcha
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
        // 几位数运算，默认是两位
        captcha.setLen(2);
        // 获取运算的结果
        String result = "";
        try {
            result = new Double(Double.parseDouble(captcha.text())).intValue() + "";
        } catch (Exception e) {
            result = captcha.text();
        }
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
        // 保存
        redisUtils.set(uuid, result, expiration, TimeUnit.MINUTES);
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return ResponseEntity.ok(imgResult);
    }

    @ApiOperation("退出登录")
    @AnonymousAccess
    @DeleteMapping(value = "/logout")
    public ResponseEntity<Object> logout(HttpServletRequest request) {
        onlineUserService.logout(tokenUtil.getToken(request));
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @AnonymousAccess
    @Log("查询门店列表根据用户账号")
    @ApiOperation("查询门店列表根据用户账号")
    @PostMapping(value = "/queryByAccount")
//    @PreAuthorize("@el.check('supplier:edit')")
    public ResponseEntity<Object> queryByAccount(String username) {
        if (CharUtil.isEmpty(username)) {
            throw new ServiceException("用户名不能为空");
        }
        User user = userService.getOne(Wrappers.<User>query().lambda().eq(User::getUsername, username));
        Map<String, Object> map = new HashMap<>();
        if (CharUtil.isEmpty(user)) {
            map.put("success", false);
        } else {
            map.put("success", true);
            map.put("list", storeService.queryListByUserId(user.getId()));
        }
        return ResponseEntity.ok(map);
    }

    @Log("返回当前用户登录门店")
    @ApiOperation("返回当前用户登录门店")
    @GetMapping(value = "/queryStoreName")
    public ResponseEntity<String> queryStoreName() {
        String pharmacyId = LoginUserUtil.getPharmId();
        Store store = storeService.getById(Long.parseLong(pharmacyId));
        return ResponseEntity.ok(store.getStoreName());
    }

    @Log("切换门店")
    @ApiOperation("切换门店")
    @PostMapping(value = "/switchStore")
    public ResponseEntity<Object> switchStore(@Validated @RequestBody SwitchStoreReq storeReq, HttpServletRequest request) {
        String pharmacyId = LoginUserUtil.getPharmId();
        if (pharmacyId.equals(storeReq.getPharmacyId())) {
            return new ResponseEntity<>(HttpStatus.OK);
        }

        JwtUser jwtUser = (JwtUser) userDetailsService.loadUserByUsername(SecurityUtils.getUsername());
        Store store = this.storeService.queryByUserId(jwtUser.getId(), storeReq.getPharmacyId());
        if (store == null) {
            throw new ServiceException("无效的药房id");
        }

        Map<String, Object> authInfo = this.startLogin(jwtUser.getUsername(), storeReq.getPassword(), storeReq.getPharmacyId(), request);
        // 销毁旧的token
        this.onlineUserService.logout(tokenUtil.getToken(request));
        return ResponseEntity.ok(authInfo);
    }

}
